#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2009, Kao Cardoso Félix
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
#   notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
#   copyright notice, this list of conditions and the following
#   disclaimer in the documentation and/or other materials provided
#   with the distribution.
# * Neither the name of the python-game-tools nor the names of its
#   contributors may be used to endorse or promote products derived
#   from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

"""This module defines a level and it's related classes.
"""
try:
    import json
except ImportError:
    import simplejson as json

import pyglet
from pyglet.gl import *


class Level(object):
    """A level is just a collection of layers that can be saved and
    loaded from disc.
    """

    def __init__(self):
        """
	"""
        self.layers = [Layer() for i in range(10)]

    def draw(self):
        """Draw the level.
        """
        for layer in self.layers:
            layer.draw()

    def save(self, file_obj):
        """Save the level to a file.
        """
        json.dump(self, file_obj, cls=LevelJSONEncoder)

    def load(self, file_obj):
        """Load a level from a file.
        """
        layers_nodes = json.load(file_obj, object_hook=level_object_hook)
        self.layers = []
        for nodes in layers_nodes:
            layer = Layer()
            layer.nodes = nodes
            self.layers.append(layer)


class Node(pyglet.sprite.Sprite):
    """A node is an object contained in a layer.
    """

    def __init__(self, img_resource, *args, **kwargs):
        """Create a Node for the given image resource.
        """
        super(Node, self).__init__(img_resource.image,
                                   *args, **kwargs)

        # FIXME: I don' like this solution. It must have a cleaner way
        # for keeping the reference to the source image of a node.
        self.src_filename = img_resource.filename
        print img_resource.image.width, img_resource.image.height
        print self._texture.tex_coords

        self._flip_x = False
        self._flip_y = False

    def draw_bounds(self, color=(255,255,255)):
        """Draw the node bounds.
        """
        old_color = self.color
        self.color = color
        self._vertex_list.draw(GL_LINE_LOOP)
        self.color = old_color

    def contains(self, p):
        """Return True if the point (x, y) is contained on the node
        bounds.
        """
        # Put point on the node local coord system
        p = p - self.position
        p = p.rotate(-self.rotation)

        return abs(p.x) < (self.width / 2) and abs(p.y) < (self.height / 2)

    def _set_flip(dir):
        attr_name = "_flip_%s" % dir

        def f(self, value):
            if bool(value) != getattr(self, attr_name % dir):
                transform = {attr_name : True}
                self.image = image.get_transform(**transform)
                setattr(self, bool(value))

        return f

    def _get_flip(dir):
        def f(self):
            attr_name = "_flip_%s" % dir
            return getattr(self, attr_name)
        return f

    flip_x = property(_get_flip("x"), _set_flip("x"),
                      """Tells if the image of the node is flipped on x.
                      """)
    flip_y = property(_get_flip("y"), _set_flip("y"),
                       """Tells if the image of the node is flipped on y.
                      """)

    del _set_flip
    del _get_flip


class Layer(object):
    """A layer to contain nodes.
    """

    def __init__(self):
        """Create an empty layer
	"""
	self.nodes = []
        self.visible = True

    def draw(self):
        """Draw the layer
        """
        if self.visible:
            for node in self.nodes:
                node.draw()

    def query_pos(self, pos):
        """Get the node at the position (x, y) on this layer.
        """
        for node in reversed(self.nodes):
            if node.contains(pos): return node

    def add_node(self, node, z=None):
        """Add a node to the layer in the given z coord. If z not
        given the node is put on top.
        """
        if z is not None:
            self.nodes.insert(z, node)
        else:
            self.nodes.append(node)

    def remove_node(self, node):
        """Removes the node from the layer. Returns it's z value at
        the moment of removal.
        """
        z = self.nodes.index(node)
        self.nodes.remove(node)

        return z

class ParallaxLayer(Layer):
    """A layer with parallax effect
    """

    def __init__(self, distance, camera):
        """
	"""
        super(ParallaxLayer, self).__init__()
        self.distance = distance
        self.camera = camera

    def draw(self):
        """Draw the parallax layer.
        """
        t = (self.camera.pos / self.distance) - self.camera.pos
        glPushMatrix()
        glTranslatef(-t.x, -t.y, 0.0)
        super(ParallaxLayer, self).draw()
        glPopMatrix()


class ImageResource(object):
    """A little class to represent an image that come from a file.
    """

    def __init__(self, filename, center_anchor=True):
        """Create the resource from the file named filename.
	"""
	self.image = pyglet.resource.image(filename)
        self.filename = filename

        if center_anchor:
            self.image.anchor_x = self.image.width // 2
            self.image.anchor_y = self.image.height // 2


class LevelJSONEncoder(json.JSONEncoder):
    """JSON Encoder for level objects.
    """

    def default(self, o):
        if isinstance(o, Level):
            return o.layers
        elif isinstance(o, Layer):
            return o.nodes
        elif isinstance(o, Node):
            return {
                "image" : o.src_filename,
                "position" : list(o.position),
                "rotation" : o.rotation,
                "scale" : o.scale,
                "opacity" : o.opacity,
                "flip_x" : o.flip_x,
                "flip_y" : o.flip_y
            }

        return super(LevelJSONEncoder, self).default(o)


def level_object_hook(dct):
    """The object hook for loading a level.
    """
    img_resource = ImageResource(dct.pop("image"))
    x, y = dct.pop("position")

    node = Node(img_resource, x=x, y=y)
    node.__dict__.update(dct)

    return node
